package com.sunxiansheng.oss.adapter;

import com.sunxiansheng.oss.entity.FileInfo;
import com.sunxiansheng.oss.util.MinioUtil;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.List;

/**
 * MinioStorageAdapter类实现了StorageAdapter接口，使用MinioUtil类与MinIO对象存储服务进行交互。
 * 提供了一系列方法来管理存储桶和对象，包括创建桶、上传文件、下载文件、删除文件等操作。
 * @Author sun
 * @Create 2024/5/31 10:06
 * @Version 1.0
 */
@Component
public class MinioStorageAdapter implements StorageAdapter {

    @Resource
    private MinioUtil minioUtil; // 使用MinioUtil工具类来执行存储操作

    /**
     * MinIO服务的URL。
     * 该URL通常在配置文件中设置，用于构建文件访问的完整URL。
     */
    @Value("${minio.url}")
    private String url;

    /**
     * 创建存储桶。
     * 使用MinioUtil工具类创建存储桶。
     *
     * @param bucket 存储桶的名称
     */
    @Override
    @SneakyThrows
    public void createBucket(String bucket) {
        minioUtil.createBucket(bucket);
    }

    // ============================== 文件上传后的URL：url + 桶名 + 上传时的objectName ==============================
    /**
     * 上传文件到指定存储桶，并返回可访问的url
     * 使用提供的对象名称或文件的原始名称进行上传。
     *
     * @param uploadFile 要上传的文件
     * @param bucket 存储桶的名称
     * @param objectName 对象名称，表示完整的文件路径和名称
     */
    @Override
    @SneakyThrows
    public String uploadFile(MultipartFile uploadFile, String bucket, String objectName) {
        minioUtil.createBucket(bucket);
        String finalObjectName = generateObjectName(uploadFile.getOriginalFilename(), objectName);
        minioUtil.uploadFile(uploadFile.getInputStream(), bucket, finalObjectName);
        return getUrl(uploadFile.getOriginalFilename(), bucket, objectName);
    }

    /**
     * 自定义对象名的格式，上传时的对象名是什么格式，那么下载时的对象名也是什么格式
     * url的格式就是 url + 桶名 + 对象名
     *
     * @param originalFilename 原始文件名字
     * @param objectName 提供的对象名称
     * @return 最终用于存储的对象名称
     */
    private String generateObjectName(String originalFilename, String objectName) {
        // 如果对象名为空，则使用文件的原始名称作为对象名
        if (objectName == null) {
            return originalFilename;
        }
        // 如果对象名不为空，则对象名 + / + 文件名作为对象名
        return objectName + "/" + originalFilename;
    }

    /**
     * 获取文件的访问URL。
     * 该URL可以用于在浏览器中查看或下载文件。
     *
     * @param bucket 存储桶的名称
     * @param objectName 对象名称，即文件在存储桶中的名称
     * @return 文件的完整URL
     */
    @Override
    @SneakyThrows
    public String getUrl(String originalFilename, String bucket, String objectName) {
        // 首先生成对象名
        String finalObjectName = generateObjectName(originalFilename, objectName);
        // url的格式就是 url + 桶名 + 对象名
        return url + "/" + bucket + "/" + finalObjectName;
    }
    // ============================== 文件上传后的URL：url + 桶名 + 上传时的objectName ==============================

    /**
     * 获取所有存储桶的名称列表。
     *
     * @return 存储桶名称的列表
     */
    @Override
    @SneakyThrows
    public List<String> getAllBucket() {
        return minioUtil.getAllBucket();
    }

    /**
     * 获取指定存储桶中的所有文件信息。
     *
     * @param bucket 存储桶的名称
     * @return 文件信息列表，其中包含文件名、是否为目录标志和ETag
     */
    @Override
    @SneakyThrows
    public List<FileInfo> getAllFile(String bucket) {
        return minioUtil.getAllFile(bucket);
    }

    /**
     * 从指定存储桶下载文件。
     *
     * @param bucket 存储桶的名称
     * @param objectName 对象名称，即文件在存储桶中的名称
     * @return 文件输入流，用于读取下载的文件内容
     */
    @Override
    @SneakyThrows
    public InputStream downLoad(String bucket, String objectName) {
        return minioUtil.downLoad(bucket, objectName);
    }

    /**
     * 删除指定存储桶。
     * 注意：存储桶必须为空才能被删除。
     *
     * @param bucket 存储桶的名称
     */
    @Override
    @SneakyThrows
    public void deleteBucket(String bucket) {
        minioUtil.deleteBucket(bucket);
    }

    /**
     * 删除指定存储桶中的文件。
     *
     * @param bucket 存储桶的名称
     * @param objectName 对象名称，即要删除的文件在存储桶中的名称
     */
    @Override
    @SneakyThrows
    public void deleteObject(String bucket, String objectName) {
        minioUtil.deleteObject(bucket, objectName);
    }

}